#ifndef __QTCH_DB_POSTGRESQL_H__
#define __QTCH_DB_POSTGRESQL_H__

#include <libpq-fe.h>
#include <vector>
#include <map>
#include <list>
#include "qtch/mutex.h"
#include "qtch/db/db.h"
#include "qtch/singleton.h"


namespace qtch {

bool postgresql_timestamp_to_time_t(const char* timestamp,time_t& ts);
bool postgresql_timestamp_to_time_t(const std::string& timestamp,time_t& ts);
bool time_to_postgresql_timestamp(time_t time,std::string& timestamp);

class PostgresqlRes : public ISQLData {
public:
    typedef std::shared_ptr<PostgresqlRes> ptr;
    ~PostgresqlRes(){}
    PostgresqlRes(PGresult* res);
    int getErrno() const override;
    const std::string getErrStr() const override;
    int getDataCount() override;
    int getColumnCount() override;
    int getColumnBytes(int idx) override;
    int getColumnType(int idx) override;
    int getColumnIdx(const std::string& columnName);
    int getColumnIdx(const char* columnName);
    std::string getColumnName(int idx) override;
    bool isNull(int idx) override;
    int8_t getInt8(int idx) override;
    uint8_t getUint8(int idx) override;
    int16_t getInt16(int idx) override;
    uint16_t getUint16(int idx) override;
    int32_t getInt32(int idx) override;
    uint32_t getUint32(int idx) override;
    int64_t getInt64(int idx) override;
    uint64_t getUint64(int idx) override;
    float getFloat(int idx) override;
    double getDouble(int idx) override;
    std::string getString(int idx) override;
    std::string getBlob(int idx) override;
    time_t getTime(int idx) override;
    bool isNull(const std::string& name) override;
    int8_t getInt8(const std::string& name) override;
    uint8_t getUint8(const std::string& name) override;
    int16_t getInt16(const std::string& name) override;
    uint16_t getUint16(const std::string& name) override;
    int32_t getInt32(const std::string& name) override;
    uint32_t getUint32(const std::string& name) override;
    int64_t getInt64(const std::string& name) override;
    uint64_t getUint64(const std::string& name) override;
    float getFloat(const std::string& name) override;
    double getDouble(const std::string& name) override;
    std::string getString(const std::string& name) override;
    std::string getBlob(const std::string& name) override;
    time_t getTime(const std::string& name) override;
    bool next() override;

private:
    int m_errno;
    std::string m_errstr;
    int m_curIndex;
    std::shared_ptr<PGresult> m_data;
};

class PostgreSQL;
class PostgresqlStmt : public IStmt {
public:
    typedef std::shared_ptr<PostgresqlStmt> ptr;

    static PostgresqlStmt::ptr Create(std::shared_ptr<PostgreSQL> db);
    static PostgresqlStmt::ptr Create(std::shared_ptr<PostgreSQL> db,const std::string & stmt);
    ~PostgresqlStmt();
    virtual int bindInt8(int idx, const int8_t& value) override;
    virtual int bindUint8(int idx, const uint8_t& value) override;
    virtual int bindInt16(int idx, const int16_t& value) override;
    virtual int bindUint16(int idx, const uint16_t& value) override;
    virtual int bindInt32(int idx, const int32_t& value) override;
    virtual int bindUint32(int idx, const uint32_t& value) override;
    virtual int bindInt64(int idx, const int64_t& value) override;
    virtual int bindUint64(int idx, const uint64_t& value) override;
    virtual int bindFloat(int idx, const float& value) override;
    virtual int bindDouble(int idx, const double& value) override;
    virtual int bindString(int idx, const char * value) override;
    virtual int bindString(int idx, const std::string& value) override;
    virtual int bindBlob(int idx, const char* value, int64_t size) override;
    virtual int bindBlob(int idx, const std::string& value) override;
    virtual int bindTime(int idx, const time_t& value) override;
    virtual int bindNull(int idx) override;

    virtual int execute() override;
    virtual int64_t getLastInsertId() override;
    virtual ISQLData::ptr query() override;

    virtual void setSqlSentence(const std::string& sql);

    virtual int getError() override;
    virtual std::string getErrStr() override;

private:
    void checkParamMemory(int idx);
    PostgresqlStmt(std::shared_ptr<PostgreSQL> db);
    PostgresqlStmt(std::shared_ptr<PostgreSQL> db,const std::string & stmt);

private:
    std::vector<char*> m_paramValues;
    std::vector<int> m_paramLengths;
    int m_paramNum;
    std::string m_sql;
    std::shared_ptr<PostgreSQL> m_postgresql;
};

class PostgresqlManager;
class PostgreSQL : public IDB ,public std::enable_shared_from_this<PostgreSQL>{
friend PostgresqlManager;
public:
    typedef std::shared_ptr<PostgreSQL> ptr;

    PostgreSQL(const std::string& name,std::map<std::string, std::string>& args);

    std::shared_ptr<PGconn> getConn() const {return m_conn;}
    int32_t getPoolSize() const {return m_poolSize;}
    void setPoolSize(int32_t v) {m_poolSize = v;}
    uint64_t getLastUsedTime() const {return m_lastUsedTime;}

    bool connect();
    bool ping();
    bool isNeedCheck();
    bool reConnect();

    int64_t getLastInsertId()override;
    IStmt::ptr prepare(const std::string& stmt) override;
    int getError() override;
    std::string getErrStr() override;
    ITransaction::ptr openTransaction(bool auto_commit = false) override;
    ISQLData::ptr query(const char* format, ...) override;
    ISQLData::ptr query(const char* format, va_list ap);
    ISQLData::ptr query(const std::string& sql) override;
    int execute(const char* format, ...) override;
    int execute(const char* format, va_list ap);
    int execute(const std::string& sql) override;

private:
    std::shared_ptr<PGconn> m_conn;
    std::map<std::string, std::string> m_params;
    std::string m_dbname;
    uint64_t m_lastUsedTime;
    int32_t m_poolSize;
    bool m_hasError;


};

class PostgresqlTransaction : public ITransaction {
public:
    typedef std::shared_ptr<PostgresqlTransaction> ptr;

    static PostgresqlTransaction::ptr Create(PostgreSQL::ptr pq,bool autoCommit);
    ~PostgresqlTransaction();
    bool begin() override;
    bool commit() override;
    bool rollback() override;

    int execute(const char* format, ...) override;
    int execute(const char* format, va_list ap);
    int execute(const std::string& sql) override;
    int64_t getLastInsertId() override;

    PostgreSQL::ptr getPostgresql() const {return m_pq;}
    bool isAutoCommit() const {return m_autoCommit;}
    bool isFinish() const {return m_isfinish;}
    bool hasError() const {return m_hasError;}

private:
    PostgresqlTransaction(PostgreSQL::ptr pq,bool autoCommit);

private:
    PostgreSQL::ptr m_pq;
    bool m_autoCommit;
    bool m_isfinish;
    bool m_hasError;

};

class PostgresqlManager {
public:
    typedef Mutex MutexType;

    PostgresqlManager();
    ~PostgresqlManager();

    PostgreSQL::ptr get(const std::string& name);
    void registerPostgresql(const std::string& name,const std::map<std::string,std::string>& define);
    void checkConnect(uint64_t sec = 30);

    ISQLData::ptr query(const std::string& name,const char* format, ...);
    ISQLData::ptr query(const std::string& name,const char* format, va_list ap);
    ISQLData::ptr query(const std::string& name,const std::string& sql);
    int execute(const std::string& name,const char* format, ...);
    int execute(const std::string& name,const char* format,va_list ap);
    int execute(const std::string& name,const std::string& sql);

    uint32_t getMaxConn() const {return m_maxConn;}
    void setMaxConn(uint32_t v)  {m_maxConn = v;}


private:
    void freePQ(const std::string& name,PostgreSQL* m);

private:
    std::map<std::string, std::list<PostgreSQL*> > m_pqs;
    uint32_t m_maxConn;
    std::map<std::string ,std::map<std::string,std::string> > m_defines;
    MutexType m_mutex;

};

typedef qtch::Singleton<PostgresqlManager> PostgresqlMgr;



}


#endif